package com.cx.aspec;

import com.cx.annotation.TxTans;
import com.cx.config.LcnConnection;
import com.cx.config.TransThread;
import com.cx.redis.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.interceptor.*;

import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Aspect
@Component
@Slf4j
public class TranAspec {
    public static ThreadLocal<String> TRANSKEY = ThreadLocal.withInitial(String::new);

    public static ThreadLocal<LcnConnection> TRANS_CONN_LOCAL = new ThreadLocal<>();

    private static final String exp = "@annotation(com.cx.annotation.TxTans)";

    @Pointcut(exp)
    public void txTransactionPointcut() {
    }

    @Around("execution(* javax.sql.DataSource.getConnection(..))")
    public Object connAround(ProceedingJoinPoint point) throws Throwable {
        LcnConnection lcnConnection = new LcnConnection((Connection) point.proceed());
        TRANS_CONN_LOCAL.set(lcnConnection);
        return lcnConnection;
    }


    @Around(value = exp)
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        //TransactionAspectSupport.
        MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
        Method method = methodSignature.getMethod();
        Class<?> targetClass = proceedingJoinPoint.getTarget().getClass();
        Method thisMethod = targetClass.getMethod(method.getName(), method.getParameterTypes());
        Object[] args = proceedingJoinPoint.getArgs();

        LcnConnection connection = TRANS_CONN_LOCAL.get();


        TxTans annotation = thisMethod.getAnnotation(TxTans.class);
        if (annotation!=null){
            //是否是发起方
            if (annotation.isStart()){
                String k = "trans:"+UUID.randomUUID().toString();
                RedisUtil.setObject(k,"wait",1,TimeUnit.HOURS);
                TRANSKEY.set(k);
            }
        }

//        if ("qwe".equals(args[0])){
//            for (;;){
//                System.out.println("]]]]]");
//            }
//        }


        Object obj = null;
        try {
            obj = proceedingJoinPoint.proceed(); //可以加参数
        } catch (Throwable throwable) {
            String k = TRANSKEY.get();
            RedisUtil.setObject(k,"rollback");
            log.error("执行失败",throwable);

           // throw throwable;
        }

        if (annotation!=null){
            //是否是发起方  并且执行成功
            if (annotation.isStart()){
                String k = TRANSKEY.get();
                String r = RedisUtil.getObject(TRANSKEY.get());
                if (!"rollback".equalsIgnoreCase(r)){
                    RedisUtil.setObject(k,"commit");
                    log.error("发起方标记成功>>>>>");
                }

            }
        }


        TransThread transThread = new TransThread(TRANSKEY.get(), connection);
        transThread.start();


        TRANSKEY.set("");

        return obj;
    }


    @Autowired
    private PlatformTransactionManager transactionManager;

    @Bean
    public TransactionInterceptor txAdvice() {
        //TransactionAspectSupport.currentTransactionStatus().
        //只读事务
        DefaultTransactionAttribute txAttr_REQUIRED_READONLY = new DefaultTransactionAttribute();
        txAttr_REQUIRED_READONLY.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        txAttr_REQUIRED_READONLY.setReadOnly(true);

        //一般事务
        RuleBasedTransactionAttribute requireRule=new RuleBasedTransactionAttribute();
        requireRule.setRollbackRules(Collections.singletonList(new RollbackRuleAttribute(Exception.class)));
        requireRule.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        //超时则回滚事务 秒
        requireRule.setTimeout(10);

        NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
        source.addTransactionalMethod("*", requireRule);
//        source.addTransactionalMethod("save*", txAttr_REQUIRED);
//        source.addTransactionalMethod("delete*", txAttr_REQUIRED);
//        source.addTransactionalMethod("update*", txAttr_REQUIRED);
//        source.addTransactionalMethod("exec*", txAttr_REQUIRED);
//        source.addTransactionalMethod("set*", txAttr_REQUIRED);
//        source.addTransactionalMethod("get*", txAttr_REQUIRED_READONLY);
//        source.addTransactionalMethod("query*", txAttr_REQUIRED_READONLY);
//        source.addTransactionalMethod("find*", txAttr_REQUIRED_READONLY);
//        source.addTransactionalMethod("list*", txAttr_REQUIRED_READONLY);
//        source.addTransactionalMethod("count*", txAttr_REQUIRED_READONLY);
//        source.addTransactionalMethod("is*", txAttr_REQUIRED_READONLY);
        return new TransactionInterceptor(transactionManager, source);
    }

    @Bean
    public Advisor txAdviceAdvisor() {
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression(exp);
        return new DefaultPointcutAdvisor(pointcut, txAdvice());
    }


}
